#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define BUFFER_SIZE 4
#define ITEM_COUNT (BUFFER_SIZE * 2)

char buffer1[BUFFER_SIZE];
int in1, out1;

char buffer2[BUFFER_SIZE];
int in2, out2;

typedef struct {
    int value;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} sema_t;  // 信号量

sema_t mutex1_sema, mutex2_sema;
sema_t empty_buffer1_sema, empty_buffer2_sema;
sema_t full_buffer1_sema, full_buffer2_sema;

void sema_init(sema_t *sema, int value) {
    sema->value = value;
    pthread_mutex_init(&sema->mutex, NULL);
    pthread_cond_init(&sema->cond, NULL);
}

void sema_wait(sema_t *sema) {
    pthread_mutex_lock(&sema->mutex);
    while (sema->value <= 0)
        pthread_cond_wait(&sema->cond, &sema->mutex);
    sema->value--;
    pthread_mutex_unlock(&sema->mutex);
}

void sema_signal(sema_t *sema) {
    pthread_mutex_lock(&sema->mutex);
    ++sema->value;
    pthread_cond_signal(&sema->cond);
    pthread_mutex_unlock(&sema->mutex);
}

char get_item(char buffer[], int *out) {
    char item;
    item = buffer[*out];
    *out = (*out + 1) % BUFFER_SIZE;
    return item;
}

void put_item(char buffer[], int *in, char item) {
    buffer[*in] = item;
    *in = (*in + 1) % BUFFER_SIZE;
}

void *consume(void *arg) {
    int i;
    char item;
    for(i = 0; i < ITEM_COUNT; i ++) {
        sema_wait(&full_buffer2_sema);
        sema_wait(&mutex2_sema);
        
        item = get_item(buffer2, &out2);
        printf("        %c\n", item);

        sema_signal(&mutex2_sema);
        sema_signal(&empty_buffer2_sema);
    }
    return NULL;
}

void *compute(void *arg) {
    int i;
    char item;
    for(i = 0; i < ITEM_COUNT; i ++) {
        sema_wait(&full_buffer1_sema);
        sema_wait(&mutex1_sema);

        item = get_item(buffer1, &out1);

        sema_signal(&mutex1_sema);
        sema_signal(&empty_buffer1_sema);


        sema_wait(&empty_buffer2_sema);
        sema_wait(&mutex2_sema);

        put_item(buffer2, &in2, item - 32);
        printf("    %c:%c\n", item, item - 32);

        sema_signal(&mutex2_sema);
        sema_signal(&full_buffer2_sema);
    }
    return NULL;
}

void *produce(void *arg) {
    int i;
    char item;
    for(i = 0; i < ITEM_COUNT; i ++) {
        sema_wait(&empty_buffer1_sema);
        sema_wait(&mutex1_sema);

        item = 'a' + i;
        put_item(buffer1, &in1, item);
        printf("%c\n", item);

        sema_signal(&mutex1_sema);
        sema_signal(&full_buffer1_sema);
    }
    return NULL;
}

int main() {
    pthread_t computer_tid, consumer_tid;

    sema_init(&mutex1_sema, 1);
    sema_init(&mutex2_sema, 1);
    sema_init(&empty_buffer1_sema, BUFFER_SIZE - 1);
    sema_init(&empty_buffer2_sema, BUFFER_SIZE - 1);
    sema_init(&full_buffer1_sema, 0);
    sema_init(&full_buffer2_sema, 0);
    
    pthread_create(&computer_tid, NULL, compute, NULL);
    pthread_create(&consumer_tid, NULL, consume, NULL);
    produce(NULL);
    
    pthread_join(consumer_tid, NULL);    
    return 0;
}